if(!QS) {
    var QS = {};
}
if(typeof(GOLBAL_DATA)=="undefined"){
	GOLBAL_DATA={};
}
if(typeof(GOLBAL_DATA.showAfter)=="undefined"){
	GOLBAL_DATA.showAfter=1;
}
if(!push){
    var push = {};
}
if(!pull){
    var pull={};
}

if (!push.subSeparator) {
    push.subSeparator = "_";
}
if (!pull.subSeparator) {
    pull.subSeparator = "_";
}
QS.DataHandler=function(type){
    this.pause = false;
    this.connectionStatus=false;
    this.defaultModuleType='default';
    this.type=type;
    this._id=-1;
    this.widgets = {};    // 0:whatchlist 1:chart 2:news 3:quicktake 4:alert 5:tickertape 6:l2MarketDepth 7:time&sales 8:options 10:quotes
    
	var genDateTime=function(marketPriceMap,d){
		if(!marketPriceMap['Date']){// if package is not contains the date , will get it from cache.[gData]
			if(d['Date']&&d['Time']){
				marketPriceMap['Date']=d['Date'].replace(/-/g,'');
				marketPriceMap['Time']=d['Time'];
			}
		}
	};
	
    var  calculateChg=function(needUpdateField,d,securityType){
        var chg,ochg,chgp,ochgp;
        if(securityType=="20") {//forex  5 
            if(typeof needUpdateField['BidPrice']!='undefined'&&d['BidPrice']!=d['LastPrice']){
                needUpdateField['LastPrice']={
                    value:d['BidPrice'],
                    oValue:d['LastPrice'],
                    dataType:'f'
                };
                d['LastPrice']=d['BidPrice'];
            }   
            if(typeof needUpdateField['BidPrice']!='undefined'||typeof needUpdateField['ClosePrice']!='undefined'){
                if(typeof d['ClosePrice']!='undefined'&&typeof d['BidPrice']!='undefined'){
                    chg=d['BidPrice']-d['ClosePrice'];    
                    chgp=chg/d['ClosePrice']*100;
                    ochg=d['Chg'];
                    ochgp=d['Chg%'];
                    if (chg != ochg) {
                        d['Chg'] = chg;
                        d['Chg%'] = chgp;
                    }
                }
            }
            if(typeof needUpdateField['BidPrice']!='undefined'||typeof needUpdateField['AskPrice']!='undefined'){
                if(typeof d['BidPrice']!='undefined'&&typeof d['AskPrice']!='undefined'){
                    var sp=Math.abs(d['BidPrice']-d['AskPrice']);
                    if(sp!=d['Spread']){
                        needUpdateField['Spread']={
                            value:sp,
                            oValue:d['Spread'],
                            dataType:'f'
                        };
                        d['Spread']=sp;
                    }
                }
            }
        }else{
            if(d['showAfter'] == 1){
				if ((typeof needUpdateField['AfterTPrice']!='undefined'||typeof needUpdateField['LastPrice']!='undefined') && typeof d['LastPrice']!='undefined'&&typeof d['AfterTPrice']!='undefined') {
                    chg = d['AfterTPrice'] - d['LastPrice'];
                    chgp = chg / d['LastPrice'] * 100;
                    ochg = d['AfterChg'];
                    ochgp = d['AfterChg%'];
                    if (chg != ochg) {
                        d['AfterChg'] = chg;
                        d['AfterChg%'] = chgp;
                    }
                }
            }else{
                if ((typeof needUpdateField['LastPrice']!='undefined' || typeof needUpdateField['ClosePrice']!='undefined')&&typeof d['ClosePrice']!='undefined' && typeof d['LastPrice']!='undefined') { 
                    chg = d['LastPrice'] - d['ClosePrice'];
                    chgp = chg / d['ClosePrice'] * 100;
                    ochg = d['Chg'];
                    ochgp = d['Chg%'];
                    if (chg != ochg) {
                        d['Chg'] = chg;
                        d['Chg%'] = chgp;
                    }    
                }
            }
        }
        if (d['showAfter']==1) {
			if(typeof needUpdateField['LastPrice']!='undefined'){// if needupdateField contains LastPrice on pre-after, in fact, we need update Closeprice.
                needUpdateField['ClosePrice'] = needUpdateField['LastPrice'];
                delete needUpdateField['LastPrice'];
            }else{
                delete needUpdateField['ClosePrice'];
            }
			
            if(typeof needUpdateField['AfterTPrice']!='undefined'){//if needUpdateField contains LastPrice on pre-after, in fact, we need update LastPrice.
                needUpdateField['LastPrice'] = needUpdateField['AfterTPrice'];
                needUpdateField["LastPrice"].isAfter = true;
                delete needUpdateField['AfterTPrice'];
            }else{
                delete needUpdateField['LastPrice'];
            }
			
            if(typeof needUpdateField['AfterTTime']!='undefined'){
                needUpdateField['Time'] = needUpdateField['AfterTTime'];
                delete needUpdateField['AfterTTime'];
            }else{
                delete needUpdateField['Time'];
            }
            if(typeof needUpdateField['AfterTDate']!='undefined'){
                needUpdateField['Date'] = needUpdateField['AfterTDate'];
                delete needUpdateField['AfterTDate'];
            }else{
                delete needUpdateField['Date'];
            }
            if(typeof needUpdateField['AfterTxt']!='undefined'&&needUpdateField['AfterTxt'].value=='After Hours'){// first after price arrive. send the last price as previou close price.
                needUpdateField["ClosePrice"] = {
                    dataType: "f",
                    value: d["LastPrice"],
                    isAfter: true
                };
            }
        }else{
            deleteAfterData(needUpdateField);
        }
        if (chg != ochg) {
            needUpdateField['Chg'] = {
                value: chg,
                oValue: ochg,
                dataType: 'f'
            };
            needUpdateField['Chg%'] = {
                value: chgp,
                oValue: ochgp,
                dataType: 'f'
            };
            needUpdateField['Alert'] = {value: true,oValue: false,dataType:'b'};
        }
    };
    
    var deleteAfterData=function(map){
        delete map["AfterTDate"];
        delete map["AfterTTime"];
        delete map["AfterTPrice"];
    };
    
    var deleteTradeData=function(map){
        delete map["TradeDate"];
        delete map["TradeTime"];
        delete map["LastPrice"];
    };
    
    var setAfterTxt=function(serviceName,gKey,time,needUpdateField){
        var txt='';
        if(parseInt(time,10) >= gExchangeMarketTimeMap[serviceName][1]*60+60){  // feed 3.0 change the time's unit to second.
            txt = "After Hours";
        }else if(parseInt(time,10) <= gExchangeMarketTimeMap[serviceName][0]*60){
            txt = "Pre-Market";
        }
        if(txt!=""){   // first extend hour price.
            needUpdateField["AfterTxt"]={value:txt,oValue:gData[gKey]["AfterTxt"],dataType:'s'};
            gData[gKey]["AfterTxt"]=txt;
        }
    };
    
    this.updateChartWidget=function(gKey,dataType,marketPriceMap){
        var lastPrice=marketPriceMap["LastPrice"],tradeDate=marketPriceMap["TradeDate"],tradeTime=marketPriceMap["TradeTime"],volume=marketPriceMap["Volume"];
        if(lastPrice>0||volume>0){
            var needRefreshChartFields={
                LastPrice:{value:parseFloat(lastPrice,10),dataType:'f'},
                Volume:{value:parseInt(volume,10),dataType:'i'}
            };
            if(tradeDate){
                needRefreshChartFields["TradeDate"]={value:formatDateString(tradeDate),dataType:'s'};
            }
            if(tradeTime){
                needRefreshChartFields["Time"]={value:parseInt(tradeTime, 10),dataType:'i'};
            }
            if (((dataType=="R"&&marketPriceMap['LastMarket']!=17)||this.type=='push')&&!$.isEmptyObject(needRefreshChartFields)) {  // update chart component data. if source is R, lastMarket is 17 ,is BATS
                if(typeof GB_ChartDataManager!='undefined'){                                       // delayed pull data is from other interface.
                    GB_ChartDataManager.updateData(gKey,needRefreshChartFields);
                }        
            }
        }
    };
    
    this.getMarketQuoteData=function(gkey,field){  // get last price,chg%,chg,time and date from gData. 
        if(!gData[gkey])return null;
        var oData = gData[gkey],data = {};
        if(typeof field!='undefined'&&
           !(field=="LastPrice"||field=="Chg"||field=="ClosePrice"||field=="Chg%"||field=="Time"||field=="Date"||field=="R/D")){
            return {value: oData[field]};
        }
        if(oData["showAfter"]==1){   // show extend data,only relevant to showAfter.
            data["LastPrice"] = {dataType:"f",value:oData["AfterTPrice"],isAfter:true};
            data["Chg"] = {dataType:"f",value:oData["AfterChg"] }; //oData["AfterChg"];
            data["Chg%"] = {dataType:"f",value:oData["AfterChg%"] };  //oData["AfterChg%"];
            data["Time"] = {dataType:"f",value:oData["AfterTTime"] }; //oData["AfterTTime"];
            data["Date"] = {dataType:"f",value:oData["AfterTDate"] }; //oData["AfterTDate"];
            data["ClosePrice"]={dataType:"f",value:oData["LastPrice"] };//oData["LastPrice"];
        } else{
            data["LastPrice"] = {dataType:"f",value:oData["LastPrice"] };
            data["Chg"] = {dataType:"f",value:oData["Chg"] }; //oData["AfterChg"];
            data["Chg%"] = {dataType:"f",value:oData["Chg%"] };  //oData["AfterChg%"];
            data["Time"] = {dataType:"f",value:oData["Time"] }; //oData["AfterTTime"];
            data["Date"] = {dataType:"f",value:oData["Date"] }; //oData["AfterTDate"];
            data["ClosePrice"]={dataType:"f",value:oData["ClosePrice"] };//oData["LastPrice"];
        }
        data["showAfter"] = {dataType:"i",value:oData["showAfter"]};
        data["AfterTxt"] = {dataType:"s",value:oData["AfterTxt"]};
        data["R/D"] = {dataType:"s",value:oData["R/D"] };
        return typeof field == 'undefined'? data:data[field];
    };
    
    this.updateMarketPrice=function(body){
        var itemsReceived = body.items;
		
        var result = {}, aL, gKey, marketPriceMap, fieldsForItem,tempAL;
        for (var i = 0, itemLh = itemsReceived.length; i < itemLh; i++) {
            result.sourceType = itemsReceived[i].source;
            result.serviceName = itemsReceived[i].service;
            result.itemName = itemsReceived[i].symbol;
            result.dataType = result.sourceType; // D or R.
            oExch = result.serviceName;
            //if (result.serviceName == "34" || result.serviceName == "17" || result.serviceName == "14" || result.serviceName == "19" || result.serviceName == "1") { // bats data 17.
            //    result.serviceName = "126";
            //}
            if (this.type=="push"){
                aL = result.itemName.split(push.subSeparator);
            }else{
                tempAL=result.itemName.split(pull.subSeparator);
                if(tempAL.length==3){
                    aL = tempAL;
                }else if(tempAL.length>3){// handle pull return 
                    aL=[];
                    aL[0]=tempAL[0];
                    var temp=[],tempALLength =tempAL.length;
                    for(var ti=1;ti<tempALLength-1;ti++){
                        temp.push(tempAL[ti]);
                    }
                    aL[1]=temp.join(pull.subSeparator);
                    aL[2]=tempAL[tempALLength-1];
                }
            }
            result.symbolType = aL[0];
            result.itemName = aL[1];
            result.subcode = aL[2];
			if (result.serviceName < 126&&(result.symbolType==1||result.symbolType==10||result.symbolType==8)){  // USA index, stock etf and fund
				result.serviceName = "126";
			}
            fieldsForItem = itemsReceived[i].fields;
            gKey = result.serviceName + ":" + result.itemName;
            if(!gData[gKey]){
                gData[gKey]={};
            }
            marketPriceMap = {};
            var isAfter=null,AT=0,TT=0,DT=0,gTT=0,gAT=0,gDT=0;//AT:AfterTDate and Time,TT:TradeDate and Time,DT:Date and Timeg,gTT:TT in gData,gAT:AT in gData,gDT:DT in gData.
            marketPriceMap["R/D"] = result.dataType;
            for (var j = 0, len = fieldsForItem.length; j < len; j++) {
				if(fieldsForItem[j].name=='ClosePrice'&&fieldsForItem[j].value==0){// if closeprice is 0, we will ignore it.
					continue;
				}
                marketPriceMap[fieldsForItem[j].name] = fieldsForItem[j].value || '';
            }
            if (oExch == "17") {
                marketPriceMap["LastMarket"] = "17";// bats data, set LastMarket to 17.
                delete marketPriceMap["AfterTDate"];// if bats data,ignore after/pre
                delete marketPriceMap["AfterTTime"];
                delete marketPriceMap["AfterTPrice"];
            }
            
            //step 1:check date time paris
            if ((typeof marketPriceMap['Date'] != 'undefined' ^ typeof marketPriceMap['Time'] != 'undefined')||
            (typeof marketPriceMap['TradeDate'] != 'undefined' ^ typeof marketPriceMap['TradeTime'] != 'undefined') ||
            (typeof marketPriceMap['AfterTDate'] != 'undefined' ^ typeof marketPriceMap['AfterTTime'] != 'undefined')) {
                continue;
            }
            var needUpdateField={};//,needRefreshChartFields={};
            gAT=gData[gKey]['AfterTDate']?parseInt((gData[gKey]['AfterTDate'].replace(/-/g,'')+gData[gKey]['AfterTTime']),10):0;// date :yyyy-mm-dd in gData
            gTT=gData[gKey]['TradeDate']?parseInt((gData[gKey]['TradeDate'].replace(/-/g,'')+gData[gKey]['TradeTime']),10):0;
            gDT=gData[gKey]['Date']?parseInt((gData[gKey]['Date'].replace(/-/g,'')+gData[gKey]['Time']),10):0;
            
            //check  PushToChart
            //var needPushToChart=true;//update chart login have been in advance, so this tag is unnecessary
            if(gData[gKey]["country"]!= "USA" || oExch == 126||oExch>126||oExch==30||oExch==33||oExch==41||oExch==29){ // if the data is from "126" or no USA ticker. it will push to chart.
                this.updateChartWidget(gKey,result.dataType,marketPriceMap);
            }
            //check DateTime between marketPriceMap and gData
			var needUpdateOtherWidget=true;
			
			genDateTime(marketPriceMap,gData[gKey]);//if there are no data and time, front-end will create them;
			
            if(marketPriceMap['Date']){
                DT=parseInt((marketPriceMap['Date']+marketPriceMap['Time']),10);
                if(DT>=gDT){
                    if(marketPriceMap['AfterTDate'] &&marketPriceMap['TradeDate']){
                        AT=parseInt((marketPriceMap['AfterTDate']+marketPriceMap['AfterTTime']),10);
                        TT=parseInt((marketPriceMap['TradeDate']+marketPriceMap['TradeTime']),10);
                        var atime,ttime,aflag=false;
                        if(AT>=gAT){
                            atime=AT;
                            aflag=true;
                        }else{
                            atime=gAT;
                            deleteAfterData(marketPriceMap);
                        }
                        if(TT>=gTT){
                            ttime=TT;
                        }else{
                            ttime=gTT;
                            deleteTradeData(marketPriceMap);
                        }
                        if(atime>ttime){
                            isAfter=1;
                            if(aflag) setAfterTxt(result.serviceName,gKey,marketPriceMap['AfterTTime'],needUpdateField);
                        }else{
                            isAfter=0;
                        }
                    }else if(marketPriceMap['TradeDate']){
                        TT=parseInt((marketPriceMap['TradeDate']+marketPriceMap['TradeTime']),10);
                        if(TT>=gTT){
                            if(TT>gAT){
                                isAfter=0;
                            }else{
                                isAfter=1;
                            }
                        }else{
                            deleteTradeData(marketPriceMap);
                        }
                    }else if(marketPriceMap['AfterTDate']){
                        AT=parseInt((marketPriceMap['AfterTDate']+marketPriceMap['AfterTTime']),10);
                        if(AT>=gAT){
                            if(AT>gTT){
                                isAfter=1;
                                setAfterTxt(result.serviceName,gKey,marketPriceMap['AfterTTime'],needUpdateField);
                            }else{
                                isAfter=0;
                            }
                        }else{
                            deleteAfterData(marketPriceMap);
                        }
                                        
                    }
                }else{
                    var tempmarketPriceMap={};
                    if(typeof marketPriceMap['ClosePrice']!='undefined'){//if message contains closeprice, we will keep it to update.
                        tempmarketPriceMap['ClosePrice']=marketPriceMap['ClosePrice'];
                    }
                    if(typeof marketPriceMap['LastPrice']!='undefined' && typeof gData[gKey]['LastPrice']=='undefined'){//if message contains LastPrice and gData don't contains, we will keep it to update.
                        var needUpdateFiledList=["LastVolume","TradeDate","TradeTime","Volume"];
						tempmarketPriceMap['LastPrice']=marketPriceMap['LastPrice'];
                        for(var j=0,l=needUpdateFiledList.length;j<l;j++){
                            if(typeof marketPriceMap[needUpdateFiledList[j]]!='undefined'){
                                tempmarketPriceMap[needUpdateFiledList[j]]=marketPriceMap[needUpdateFiledList[j]];
                            }
                        }
                    }
                    if(!$.isEmptyObject(tempmarketPriceMap)){
                        marketPriceMap=tempmarketPriceMap;
                    }else{
                        needUpdateOtherWidget=false;
                    }
				}
            }
            
            if(!needUpdateOtherWidget){//if DT<gDT, no need to updateOtherWidgets.
                continue;
            }
            
            if(result.symbolType!=20&& isAfter!=null && isAfter!=gData[gKey]["showAfter"]){// if it is a forex ticker, showAfter is 0;
                needUpdateField["showAfter"]={value:isAfter,oValue:gData[gKey]["showAfter"],dataType:'i'};
                gData[gKey]["showAfter"]=isAfter;
            }
            if(GOLBAL_DATA.showAfter==0){
                gData[gKey]["showAfter"]=0;
            }
//          //solve ask/bid time is older than gdata
//          if(marketPriceMap["Time"]&&gData[gKey]["Date"]&&gData[gKey]["Time"]&(marketPriceMap["Date"]+marketPriceMap["Time"]<gData[gKey]["Date"].replace(/-/g,'')+gData[gKey]["Time"])){
//              marketPriceMap={};
//          }
            var closeIsLast=false;
            for(var fieldName in marketPriceMap){
                if(fieldName=='TradeDate'){
                    ty='s';
                    marketPriceMap[fieldName] = formatDateString(marketPriceMap[fieldName]);
                    //needRefreshChartFields["TradeDate"]={value:marketPriceMap[fieldName],dataType:ty};
                }else if(fieldName=='TradeTime'){
                    ty='i';
                    marketPriceMap[fieldName] = parseInt(marketPriceMap[fieldName], 10);
                    //needRefreshChartFields["Time"]={value:marketPriceMap[fieldName],dataType:ty};
                }else if(fieldName == "LastPrice"){
                    var lastpricevalue=parseFloat(marketPriceMap[fieldName],10);
                    if(lastpricevalue<=0 && (result.serviceName=="77"||result.serviceName=="75")&&result.symbolType=="3"){//77exchange and futrue
                        closeIsLast=true;
                    }
                    marketPriceMap[fieldName] = lastpricevalue;
                    ty='f';
                    //needRefreshChartFields["LastPrice"]={value:marketPriceMap[fieldName],dataType:ty};
                }else if(fieldName == "Volume"){
                    marketPriceMap[fieldName] = parseFloat(marketPriceMap[fieldName], 10);
                    ty='i';
                    needUpdateField['Alert'] = {value: true,oValue: false,dataType:'b'};
                    //needRefreshChartFields["Volume"]={value:marketPriceMap[fieldName],oValue:gData[gKey][fieldName],dataType:ty};
                }else if ( fieldName == "AskSize" || fieldName == "BidSize" || fieldName == "LastVolume") { 
                    marketPriceMap[fieldName] = parseInt(marketPriceMap[fieldName], 10);
                    ty='i';
                }else if(fieldName == "ClosePrice"){
                    if(!closeIsLast&& (result.serviceName=="77"||result.serviceName=="75")&&result.symbolType=="3"&&gData[gKey]["LastPrice"]<=0){
                        closeIsLast=true;
                    }
                    marketPriceMap[fieldName] = parseFloat(marketPriceMap[fieldName],10);
                    ty='f';
                }else if (fieldName == "MiddlePrice" ||fieldName == "AskPrice" || 
                  fieldName == "BidPrice" ||fieldName == "HighPrice" ||
                  fieldName == "LowPrice" ||fieldName == "VWAP" ||
                  fieldName == "OpenPrice" ||fieldName == "AfterTPrice") {
                    marketPriceMap[fieldName] = parseFloat(marketPriceMap[fieldName],10);
                    ty='f';
//                    if(fieldName=="AskPrice"&&result.symbolType=="20"){  // forex;
//                        needRefreshChartFields["LastPrice"]={value:marketPriceMap[fieldName],dataType:ty};
//                    }
                }else if(fieldName=='AfterTDate'||fieldName=='Date'){
                    ty='s';
                    marketPriceMap[fieldName] = formatDateString(marketPriceMap[fieldName]);
                }else if(fieldName == "Time" || fieldName == "AfterTTime"){
                    marketPriceMap[fieldName] = parseInt(marketPriceMap[fieldName], 10);
                    ty='i'; 
                }else if (fieldName == 'Name') {// get Name field from static not push 
                    continue;
                }else {
                    ty = 's';
                }
                if(marketPriceMap[fieldName]!=gData[gKey][fieldName]){
                    needUpdateField[fieldName]={value:marketPriceMap[fieldName],oValue:gData[gKey][fieldName],dataType:ty};
                    if(!closeIsLast){
                        gData[gKey][fieldName]=marketPriceMap[fieldName];
                    }
                }                   
            }
			
			if(needUpdateField["showAfter"]&&typeof needUpdateField["Date"]=='undefined'&&typeof gData[gKey]["Date"]!='undefined'){//if showAfter changed, we will force to update Date. to Fix QS-775
				needUpdateField["Date"]={value:gData[gKey]["Date"],oValue:null,dataType:'s'};
			}
			
            if(closeIsLast&&gData[gKey]["ClosePrice"]){
                if(gData[gKey]["ClosePrice"]!=gData[gKey]["LastPrice"]){
                    needUpdateField["LastPrice"] = {value:gData[gKey]["ClosePrice"],oValue:gData[gKey]["LastPrice"],dataType:"f"};
                    gData[gKey]["LastPrice"]=gData[gKey]["ClosePrice"];
                }else{
                    delete needUpdateField["LastPrice"];
                    gData[gKey]["LastPrice"]=gData[gKey]["ClosePrice"];
                }             
            }
			
            //step 5:cal chg/chg%
            calculateChg(needUpdateField,gData[gKey],result.symbolType);
            //step 6:update UI
//            var needPushToChart=true;
//            if(gData[gKey]["country"]== "USA" && oExch != 126){ // if the data is not from "126" for USA ticker. it can't push to chart.
//                needPushToChart = false;
//            }
//            if ((result.dataType=="R"||this.type=='push')&&needPushToChart&&!$.isEmptyObject(needRefreshChartFields)) {  // update chart component data.
//                if(typeof GB_ChartDataManager!='undefined'){                                       // delayed pull data is from other interface.
//                    GB_ChartDataManager.updateData(gKey,needRefreshChartFields);
//                }        
//            }
            if (!$.isEmptyObject(needUpdateField)) {
                var needCheckAlert=false;
                if(needUpdateField["Alert"]){
                    needCheckAlert=true;
                    delete needUpdateField["Alert"];
                }
                for (var m in this.widgets) {
                  if (m == 1|| m == 2 || m == 3 || m == 4||m==7|| m == 9 || m == 12 || m == 15) {//no need update
                      continue;
                  }else{
                      for (var o in this.widgets[m]) {
                          if (this.widgets[m].hasOwnProperty(o)) {
                              if (this.widgets[m][o].pause||typeof this.widgets[m][o].updateData!='function') 
                                  continue;
                              this.widgets[m][o].updateData(gKey, needUpdateField);
                          }
                      }
                   }
                }
                if(needCheckAlert&&typeof updateWatchlistAlert == 'function'){//notify to check alert,and then delete this field.
                   updateWatchlistAlert(gKey, false);
                }
            }
        }
    };
    this.getWidget = function(type,id){
        if (this.widgets[type]&&this.widgets[type][id.toString()]) { 
            return this.widgets[type][id.toString()];
        } else {
            return {};
        }
    };
    this.getAllRegistedWidgets = function(type){
        return this.widgets[type];
    };
    this.register = function(obj) {
        if(typeof obj.type=='undefined'||obj.type==null){
            obj.type=this.defaultModuleType;
        }
        if(typeof obj.id=='undefined'||obj.id==null){
            obj.id=this._idServer(obj.type.toString());
        }
        if(!this.widgets[obj.type.toString()]){
            this.widgets[obj.type.toString()]={};
        }
        this.widgets[obj.type.toString()][obj.id] = obj;
    };

    this.unregister = function(obj) {
         delete this.widgets[obj.type.toString()][obj.id];
    };
    
    this._idServer=function(type){
        var id=this._id--;
        return type+"_"+id;
    };
};


QS.DataManager=function(config){
    this.TYPE={
        PUSH:'push',
        PULL:'pull'
    }
    this.pushCategory={
        delay:'Delay',
        mix:'Delay and BATS'
    };
    this.config={
        server:'',
        sessionID:'',
        notDeleteInfoFromGData:true,
        type:this.TYPE.PUSH,
        rate:'fast'
    };
    this.dataObj=null;
    this.dataHandler=null;
    this.initConfig(config);
    this.isIE7=$.browser.msie && $.browser.version == "7.0";
};
QS.DataManager.prototype={
    setDataMethod:function(method){
        method = method||'';
        var type,rate='';
        switch (method.toLowerCase()){
            case 'fast':
            case 'slow':
            case 'slower':
            case 'slowest':
                type = this.TYPE.PULL;
                rate = method.toLowerCase();
                break;
            default:
                type = this.TYPE.PUSH;
                break;
        }
        this.config.type=type;
        if (rate != '') {
            this.config.rate = rate;
        }
        this.connect();
    },
    initConfig:function(config){
       if(config){
          $.extend(this.config,config);
       }
    },
    connect:function(){
        this.reset();
        this.dataHandler=new QS.DataHandler(this.config.type);
        if(this.config.type == this.TYPE.PUSH){
            this._connectToPush();
        }else{
            this._connectToPull();
        }
    },
    _connectToPush:function(){
        var self=this;
        this.dataObj=new QS.Push(this.dataHandler,{
            onConnectedFailure:function(){
                if(self.isIE7){
                    self.dataObj=null;
                    self._connectToPull();
                }
            }
//          onConnectedSuccess:function(){
//          }
        });
        this.dataObj.initConnection(this.config.server,this.config.sessionID);
    },
    _connectToPull:function(){
        this.dataObj=new QS.Pull(this.dataHandler);
        this.dataObj.initConnection(this.config.rate);
    },
    reset:function(){
        this.dataObj=null;
        this.dataHandler=null;
    },
    // parameter formate: [{gkey:"",paraList:[{sourceType:"",serverName:"",itemName:"",fields:[]},isSubscribeLevelII:false]}] 
    batchSubscribe:function(list){
        if (!list || list.length == 0) 
            return;
        var channels = [];
        var gkey;
        for (var i = 0; i < list.length; i++) {
            gkey = list[i].gkey;
            if (!gkey || !gData) 
                continue;
            if (!gData[gkey]) 
                gData[gkey] = {};
            if (list[i].optionType) {
                gData[gkey]['optionType'] = list[i].optionType; // only for option ticker
                gData[gkey]['type'] = '2';//set option security type as 2 
            }
            if (list[i].forexType) {//set forex security type as 20
                gData[gkey]['type'] = list[i].forexType;
            }
            if (list[i].futureType) {//set forex security type as 3
                gData[gkey]['type'] = list[i].futureType;
            }
            if (list[i].staticType){// set static ticker(MSTAS_ALL) type as 1
                gData[gkey]['type'] = list[i].staticType;
            }
            if (!gData[gkey]["subcribeNum"]) {
                gData[gkey]["subcribeNum"] = 0;
            }
            
            if (!gData[gkey]["subcribeLevelIINum"]) {
                gData[gkey]["subcribeLevelIINum"] = 0;
            }
			if(!gData[gkey]["unsubScribe"]) {//kidy 20120209 for unsubScribe data
				if(this.config.type == this.TYPE.PUSH) {
					if (gData[gkey]["subcribeNum"] == 0 || (list[i].isSubscribeLevelII && gData[gkey]["subcribeLevelIINum"] == 0)) { // no subscribe this ticker.
						channels = channels.concat(list[i].paraList);
					}
				}else{
					var gk=list[i].gkey.split(":");
					var exch=list[i].paraList[0].serviceName;//exchCode get from subscribe list not gkey
					if(typeof(mockExchangeCode)!="undefined"&&exch.indexOf(mockExchangeCode+"_")>-1) continue;
					var pullTicker=exch+":"+gData[list[i].gkey].type+":"+gk[1];
					gData[list[i].gkey]["pullTicker"]=pullTicker;
					channels.push(pullTicker);
				}
			}
            gData[gkey]["subcribeNum"]++;
            if (list[i].isSubscribeLevelII) {
                gData[gkey]["subcribeLevelIINum"]++;
            }
        }
        this.dataObj.batchSubscribe(channels);
    },
    // parameter formate: [{gkey:"",paraList:[{sourceType:"",serverName:"",itemName:""},isSubscribeLevelII:false]}] 
    batchUnsubscribe:function(list,notDeleteInfoFromGData){
        if(typeof notDeleteInfoFromGData=='undefined'){
            notDeleteInfoFromGData=this.config.notDeleteInfoFromGData;
        }
        if (!list || list.length == 0) 
            return;
        var channels = [];
        for (var i = 0; i < list.length; i++) {
            gkey = list[i].gkey;
            if (!gData || !gData[gkey]) 
                continue;
            if (gData[gkey]["subcribeNum"]) {
                gData[gkey]["subcribeNum"]--;
            }
            if (list[i].isSubscribeLevelII && gData[gkey]["subcribeLevelIINum"]) {
                gData[gkey]["subcribeLevelIINum"]--;
            }
            if (gData[gkey]["subcribeNum"] == 0 && (!gData[gkey]["subcribeLevelIINum"] || gData[gkey]["subcribeLevelIINum"] == 0)) { // no subscribe this ticker.
				if(!gData[gkey]["unsubScribe"]){//kidy 20120209 for unsubScribe data
					channels = channels.concat(list[i].paraList);
				}
                if (!notDeleteInfoFromGData) {
                    delete gIdMap[gData[gkey]["performanceId"]];
                    delete gData[gkey];
                }
            }
            else 
                if (!gData[gkey]["subcribeLevelIINum"] || gData[gkey]["subcribeLevelIINum"] == 0) { // need unsubscribe levelII data.
                    for (var k = 0; k < list[i].paraList.length; k++) {
                        if (list[i].paraList[k]["isLevelII"]&&!gData[gkey]["unsubScribe"]) {//kidy 20120209 for unsubScribe data
                            channels.push(list[i].paraList[k]);
                        }
                    }
                }
        }
        this.dataObj.batchUnsubscribe(channels);
    },
    getWatchlist:function(id) {
        return this.dataHandler.getWidget('0',id);
    },

    getAllRegistedWatchlists:function() {
        return this.dataHandler.getAllRegistedWidgets('0');
    },

    getChart:function(id){
        return this.dataHandler.getWidget('1',id);
    },

    getAllRegistedCharts :function() {
        return this.dataHandler.getAllRegistedWidgets('1');
    },
    getNews :function(id) {
        return this.dataHandler.getWidget('2',id);
    },
    getQuickTake :function(id) {
        return this.dataHandler.getWidget('3',id);
    },
    getAllRegistedTickerTapes :function() {
        return this.dataHandler.getAllRegistedWidgets('5');
    },
    
    getL2MarketDepth :function(id) {
        return this.dataHandler.getWidget('6',id);
    },
    
    getTimeSales :function(id) {
        return this.dataHandler.getWidget('7',id);
    },
    getOptions :function(id) {
        return this.dataHandler.getWidget('8',id);
    },
    getSR :function(id) {
       return this.dataHandler.getWidget('9',id);
    },
    getQuotes :function(id) {
        return this.dataHandler.getWidget('10',id);
    },
    getMarket :function(id) {
        return this.dataHandler.getWidget('14',id);
    },
    register :function(obj){
        this.dataHandler.register(obj);
    },
    unregister :function(obj) {
        this.dataHandler.unregister(obj);
    },
    getMarketQuoteData:function(gkey,field){  // get last price,chg%,chg,time and date from gData. 
       return this.dataHandler.getMarketQuoteData(gkey,field);
    }
};

QS.Push=function(dataHandler,callback){
    var ConnectObj;
    var self=this;
//  var isConnected = false;
    this.dataHandler=dataHandler;
    this.callback=callback;
    this.initConnection=function(host, usr){
//        if (isConnected)
//            return;
        if (usr) {
            ConnectObj = jslib.connectEntitlement(operationHandler, "connectEntitlement", host, usr, "256", "1.1.1.1/net", updateHandler);
        }else{
            ConnectObj = jslib.connect(operationHandler, "connectClosure", host, updateHandler);
        }
        ConnectObj.addFields(operationHandler, "addFieldsClosure", "ClosePrice", "LastPrice", "AskPrice", "BidPrice", "AskSize", "BidSize", 
            "Time","TradeTime","Date","TradeDate", "Volume","LastVolume", "HighPrice", "LowPrice", "OpenPrice","AfterTPrice","AfterTTime","AfterTDate",
            "401", "402", "404", "405", "407", "409", "-102","LastMarket","OpenInt","VWAP","MiddlePrice","MarketCentre",
            "BidOrder_0","BidOrder_1","BidOrder_2","BidOrder_3","BidOrder_4","BidOrder_5","BidOrder_6","BidOrder_7","BidOrder_8","BidOrder_9",
            "BidOrder_10","BidOrder_11","BidOrder_12","BidOrder_13","BidOrder_14","BidOrder_15","BidOrder_16","BidOrder_17","BidOrder_18","BidOrder_19",
            "BidSize_0","BidSize_1","BidSize_2","BidSize_3","BidSize_4","BidSize_5","BidSize_6","BidSize_7","BidSize_8","BidSize_9",
            "BidSize_10","BidSize_11","BidSize_12","BidSize_13","BidSize_14","BidSize_15","BidSize_16","BidSize_17","BidSize_18","BidSize_19",
            "BidPrice_0","BidPrice_1","BidPrice_2","BidPrice_3","BidPrice_4","BidPrice_5","BidPrice_6","BidPrice_7","BidPrice_8","BidPrice_9",
            "BidPrice_10","BidPrice_11","BidPrice_12","BidPrice_13","BidPrice_14","BidPrice_15","BidPrice_16","BidPrice_17","BidPrice_18","BidPrice_19",
            "AskPrice_0","AskPrice_1","AskPrice_2","AskPrice_3","AskPrice_4","AskPrice_5","AskPrice_6","AskPrice_7","AskPrice_8","AskPrice_9",
            "AskPrice_10","AskPrice_11","AskPrice_12","AskPrice_13","AskPrice_14","AskPrice_15","AskPrice_16","AskPrice_17","AskPrice_18","AskPrice_19",
            "AskSize_0","AskSize_1","AskSize_2","AskSize_3","AskSize_4","AskSize_5","AskSize_6","AskSize_7","AskSize_8","AskSize_9",
            "AskSize_10","AskSize_11","AskSize_12","AskSize_13","AskSize_14","AskSize_15","AskSize_16","AskSize_17","AskSize_18","AskSize_19",
            "AskOrder_0","AskOrder_1","AskOrder_2","AskOrder_3","AskOrder_4","AskOrder_5","AskOrder_6","AskOrder_7","AskOrder_8","AskOrder_9",
            "AskOrder_10","AskOrder_11","AskOrder_12","AskOrder_13","AskOrder_14","AskOrder_15","AskOrder_16","AskOrder_17","AskOrder_18","AskOrder_19",
            "1900","1901","1902","1903","1904","256","257","258","259","260","261","262","263","264","265","266","267","268","269","270","271","272","273",
            "274","275","276","277","278","279","280","281","282","283","284","285","286","287","288","289","290","291","292","293","294","295","296","297",
            "298","299","300","301","302","303","304","305","306","307","308","309","310","311","312","313","314","315","316","317","318","319","320","321",
            "322","323","324","325","326","327","328","329","330","331","332","333","334","335","336","337","338","349","340","341","342","343","344","345",
            "346","347","348","349","1904", "909","910","911","912","913","914"     
        );
//        isConnected = true;
    };
    
    
    this.batchSubscribe = function(channels){
        var str = "ConnectObj.subscribe(operationHandler, \"subscribeClosure\"";
        for (var j = 0; j < channels.length; j++) {
            chn = channels[j];
            str += ", \"" + chn.sourceType + "\", \"" + chn.serviceName + "\", \"" + chn.itemName + "\""; // jacye
            if ((j + 1) % 50 == 0 || j == channels.length - 1) {
                str += ")";
                eval(str);
                str = "ConnectObj.subscribe(operationHandler, \"subscribeClosure\"";
            }
        }
    };
    
    //
    this.batchUnsubscribe = function(channels){
        var str = "ConnectObj.unsubscribe(operationHandler, \"unsubscribeClosure\"";
        for (var j = 0; j < channels.length; j++) {
            chn = channels[j];
            str += ", \"" + chn.sourceType + "\", \"" + chn.serviceName + "\", \"" + chn.itemName + "\""; // jacye
            if ((j + 1) % 50 == 0 || j == channels.length - 1) {
                str += ")";
                eval(str);
                str = "ConnectObj.unsubscribe(operationHandler, \"unsubscribeClosure\"";
            }
        }
        
    };
    
    function operationHandler(closure, eventType, event, body) {
        if (eventType == jslib.OPERATION_ERROR) {
            var err;
            switch (event) {
                case jslib.ERROR_CONNECTED:
                    err = "You are already connected";
                    break;
                case jslib.ERROR_NOT_CONNECTED:
                    err = "You are not connected";
                    break;
                case jslib.ERROR_TIMEOUT:
                    err = "Connection timeout";
                    break;
                case jslib.ERROR_HTTP:
                    err = "HTTP status error. The HTTP status is: " + body;
                    break;
                case jslib.ERROR_SERVER:
                    err = "Server side error: " + body;
                    break;
                case jslib.ERROR_DOMAIN:
                    err = "Domain error.";
                    break;
                case jslib.ERROR_SERVER_DOWN:
                    err = "Server down.";
                    break;
            }
            if(typeof self.callback.onConnectedFailure=='function'){
                self.callback.onConnectedFailure();
            }
            //alert("Error: " + closure + ", " + err);
        } else if (eventType == jslib.OPERATION_OK) {
            // everything is ok
            
            if(typeof self.callback.onConnectedSuccess=='function'){
                self.callback.onConnectedSuccess();
            }
        }
    }
    
    function updateHandler(closure, eventType, event, body) {
        if (eventType == jslib.STREAMING_ERROR) {
            alert("Streaming error=" + event + ", body=" + body);
        } else if (eventType == jslib.MESSAGE_RECEIVED) {
            switch (event) {
                case jslib.MARKET_PRICE_UPDATE:
                    updateMarketPrice(body);
                    break;
                case jslib.STATUS_UPDATE:
                    updateStatus(body);
                    break;
            }
        }
    }
    
    
    function updateMarketPrice(body){
        self.dataHandler.updateMarketPrice(body);
    }
    
    function updateStatus(body) {
        var msg = "";
        var sourceType = body.source;
        var serviceName = body.service;
        var itemName = body.symbol;
        var itemState = body.state;
        var itemStatus = body.status;
        msg += sourceType+"|"+serviceName+"|"+itemName + " is " + itemState + "|"+itemStatus+"\n";
        if (!this.pause) {
            this.pause = true;
            setTimeout(function(){self.dataHandler.pause = false; alert(msg); msg = "";  }, 30000);
        }
    }
    
};
QS.Pull = function(dataHandler){
    this.dataHandler=dataHandler;
    this.rate={
        fast:5,
        slow:10,
        slower:15,
        slowest:30
    };
    this.intervalObj=null;
    this.batchQuerySize =30;
    this.timerObj=[];
    this.rateKey=null;
    
    this.initConnection=function(rateKey){
        this.rateKey=rateKey;
        this._createIntervalObj();
    };
    
    this._createIntervalObj=function(){
        if(this.intervalObj!=null){
            clearInterval(this.intervalObj);
        }
        var self=this;
        this.intervalObj=setInterval(function(){
            var count =0,arr=[],flag = false;
            self._clearTimer();
            for(var gKey in gData){
                flag =false;
                if(typeof gData[gKey].subcribeNum!='undefined'&&gData[gKey].subcribeNum>0){
                    count ++;
                    if(!gData[gKey].pullTicker||gData[gKey].pullTicker==''){
                        var gk=gKey.split(":");
						if(typeof(mockExchangeCode)!="undefined"&&gk[0].indexOf(mockExchangeCode+"_")>-1) continue;
                        gData[gKey].pullTicker=gk[0]+":"+gData[gKey].type+":"+gk[1];
                    }
                    arr.push(gData[gKey].pullTicker);
                }
                if(count==self.batchQuerySize){      
                    with({temp:arr}){
                       self.timerObj.push(setTimeout(function(){
                           
                           self._query(temp.join("}")); 
                       },100*self.timerObj.length));
                    }   
                    flag =true;
                    count=0;
                    arr=[];             
                }
            }
            if(!flag){
                self._query(arr.join("}"));
            }
        },this.rate[this.rateKey]*1000);  
    };
    
    this._query=function(tickers){
        if (tickers == "") {
            return false
        }
        var self = this;
        $.getJSON(PULL_Path + "getPullQuote.jsp?query="+tickers+"&cb=?",
			function(o) {
				if (typeof(o)=="undefined"||o.errorcode) {
					return false
				}
				self.dataHandler.updateMarketPrice(o)
			}
		)
    };
    this._clearTimer=function(){
       for(var i=0,l=this.timerObj.length;i<l;i++){
           this.timerObj[i]=null;
           clearTimeout(this.timerObj[i]);
       }
       this.timerObj=[];    
    };
    
    this.batchSubscribe = function(channels){//['126:1:IBM','126:1:MSFT']
        var tickers;
        for(var i =0,l=channels.length;i<l;i +=this.batchQuerySize){
            tickers=channels.slice(i,i+this.batchQuerySize).join("}");
            this._query(tickers) ;
        }
    };
    
    this.batchUnsubscribe = function(channels){
        
    };
};

if (typeof GB_DataManager == 'undefined') {
    GB_DataManager=new QS.DataManager({
        server:version.push,
        sessionID:'RKS2GDU2AJY99JZFBUYI1V6768470110_1'
    });
}
QS.setDataMethod = function(data){
    GB_DataManager.setDataMethod(data);
};
push.proxy=QS.Data=GB_DataManager;

var t = 560; 
push.test = function(delay){
    window.setInterval(function(){
        if (gData) {
            var charts = push.proxy.dataHandler.widgets["1"];
            var wls = push.proxy.dataHandler.widgets["0"];
            var tickerTape = push.proxy.dataHandler.widgets["5"];
            var mDepth = push.proxy.dataHandler.widgets["6"];
            var timesales = push.proxy.dataHandler.widgets["7"];
            var options = push.proxy.dataHandler.widgets["8"];
            var quotes = push.proxy.dataHandler.widgets["10"];
            var defaultW = push.proxy.dataHandler.widgets["default"];
            var needUpdateField = {};
            var i = parseInt(Math.random() * 100 + 1);
            var value;
            t++;
            for (var d in gData) {
                if(gData[d]["subcribeNum"]==0 && gData[d]["subcribeLevelIINum"]==0)continue;
                if (i % 7 == 0) {
                    i++;
                    continue;
                }
                i++;
                var tk = gData[d];
                gData[d]["showAfter"]=0;
                var dl = d.split(":");
                var body = {
                    "items": [{
                        "source": "R",
                        "service": dl[0],
                        "symbol": tk['type'] + "_" + dl[1] + "_-1",
                        "fields": [ {
                            "name": "AskPrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "AskSize",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "BidPrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "BidSize",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "ClosePrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "HighPrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        },{
                            "name": "LastPrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "LastVolume",
                            "value": ((i % 3 == 0) ? Math.random() : -Math.random()) *1000
                        }, {
                            "name": "LowPrice",
                            "value": (i % 3 == 0) ? Math.random() : -Math.random()
                        }, {
                            "name": "MarketCentre",
                            "value": "0"
                        }, {
                            "name": "Volume",
                            "value": "41590665"
                        }]
                    }]
                }             
                push.proxy.dataHandler.updateMarketPrice(body);
            }
        }
    }, delay || 500);
};



